/**
 * TIW2 TP3 
 * Fouille de données - Mise en oeuvre
 * 
 * @author Alexandra VENET <alexandra.venet@etu.univ-lyon1.fr>
 * @author Emmanuel GAUDE <emmanuel.gaude@etu.univ-lyon1.fr>
 * @author Erwin TANASIE <erwin.tanasie@etu.univ-lyon1.fr>
 * @version 2012-12-03
 */

package tiw2.tp3;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import weka.associations.Apriori;
import weka.associations.ItemSet;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.meta.FilteredClassifier;
import weka.classifiers.trees.J48;
import weka.core.converters.ConverterUtils.DataSource;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.AddValues;
import weka.filters.unsupervised.attribute.Discretize;
import weka.filters.unsupervised.attribute.NumericToNominal;
import weka.filters.unsupervised.attribute.Remove;

public class Titanic {
	/** Données d'origine. */
	private Instances _data;
	
	/** Attributs (noms et valeurs possibles) */
	private List<Attribute> _atts;
	
	/** Classes (nom attribut et valeurs possibles) */
	private Attribute _classes;
	
	/** Tickets en doublons - pour recuperer les familles ou les personnes qui travaillent ensemble*/
	private FastVector _doubleTickets;
	
	/**
	 * Constructeur
	 * @param data URL du fichier de données original.
	 * @param training Fichier d'entrainement : true ou false ?
	 * @throws Exception
	 */
	public Titanic(String data, boolean training) throws Exception {
		/* Récupération des donnéees. --------------------------------------- */
		_data = DataSource.read(data);

		/* Application de filtres sur les données. -------------------------- */
		/* Nominalisation des attributs pclass, sibsp et parch. */
		NumericToNominal nn = new NumericToNominal();
		
		if(!training) {
			FastVector survivedVals = new FastVector();
			survivedVals.addElement("0");
			survivedVals.addElement("1");
			_data.insertAttributeAt(
		    		new Attribute("survived", survivedVals), 
		    		0
		    	);
		}
		int nnInd[] = {
				_data.attribute("survived").index(), 
				_data.attribute("pclass").index(), 
				_data.attribute("sibsp").index(), 
				_data.attribute("parch").index()
			};
		nn.setAttributeIndicesArray(nnInd);
		
		nn.setInputFormat(_data);
		_data = Filter.useFilter(_data, nn);
		
		/* Discrétisation des attributs age et fare. 
		 * On choisi de déterminer automatiquement le nombre de tas. Cette
		 * représentation semble plus pertinente pour notre recherche.
		 */
		Discretize d = new Discretize();
		
		int dInd[] = {
				_data.attribute("age").index(),
				_data.attribute("fare").index()
			};
		d.setAttributeIndicesArray(dInd);
		d.setFindNumBins(true);
		
		d.setInputFormat(_data);
		_data = Filter.useFilter(_data, d);
		
		/* Utilisation de "UNKNOWN" pour valeurs inconnues. */
	    AddValues av = new AddValues();
		av.setLabels("UNKNOWN");
		
		av.setAttributeIndex(Integer.toString(_data.attribute("age").index() + 1));
		
		av.setInputFormat(_data);
		_data = Filter.useFilter(_data, av);

		av.setAttributeIndex(Integer.toString(_data.attribute("cabin").index() + 1));
		
		av.setInputFormat(_data);
		_data = Filter.useFilter(_data, av);

		av.setAttributeIndex(Integer.toString(_data.attribute("embarked").index() + 1));
		
		av.setInputFormat(_data);
		_data = Filter.useFilter(_data, av);
		
	    setUnknown();
		
	    /* Création de l'attribut title. ------------------------------------ */
		Map<String, FastVector> splittedName = this.splitNameValues();

	    _data.insertAttributeAt(
	    		new Attribute("surname", splittedName.get("surname")), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("title", splittedName.get("title")), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("firstname", splittedName.get("firstname")), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("maidenName", splittedName.get("maidenName")), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("nickname", splittedName.get("nickname")), 
	    		_data.numAttributes()
	    	);
	    
	    FastVector nbTicketsVals = new FastVector();
	    for(int i = 0; i < 10; i++)
	    	nbTicketsVals.addElement(Integer.toString(i));
	    _data.insertAttributeAt(
	    		new Attribute("nbTickets", nbTicketsVals), 
	    		_data.numAttributes()
	    	);

	    FastVector values = new FastVector();
	    values.addElement("0");
	    values.addElement("1");
	    
	    _data.insertAttributeAt(
	    		new Attribute("noble", values), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("soldier", values), 
	    		_data.numAttributes()
	    	);
	    
	    _data.insertAttributeAt(
	    		new Attribute("religious", values), 
	    		_data.numAttributes()
	    	);
	    
	    setSplittedNameValuesInInstances();
	    setNbTickets(getTicketValues());
	    
	    /* Création de l'attribut alone ------------------------------------ */
	    _data.insertAttributeAt(
	    		new Attribute("alone",values), 
	    		_data.numAttributes()
	    	);
	    setTravelsAlone();
	    
	    /* Création de l'attribut etage ------------------------------------ */
	    FastVector valEtage = new FastVector();
	    valEtage.addElement("1");
	    valEtage.addElement("2");
	    valEtage.addElement("3");
	    valEtage.addElement("4");
	    valEtage.addElement("5");
	    valEtage.addElement("6");
	    valEtage.addElement("7");
	    valEtage.addElement("-1");
	    
	    _data.insertAttributeAt(
	    		new Attribute("etage",valEtage), 
	    		_data.numAttributes()
	    	);
	    setEtageValues();

	    _data.deleteAttributeAt(_data.attribute("name").index());
	    _data.deleteAttributeAt(_data.attribute("sibsp").index());
	    _data.deleteAttributeAt(_data.attribute("parch").index());
	    _data.deleteAttributeAt(_data.attribute("ticket").index());
	    _data.deleteAttributeAt(_data.attribute("cabin").index());
	    _data.deleteAttributeAt(_data.attribute("surname").index());
//	    _data.deleteAttributeAt(_data.attribute("title").index());
	    _data.deleteAttributeAt(_data.attribute("firstname").index());
	    _data.deleteAttributeAt(_data.attribute("maidenName").index());
	    _data.deleteAttributeAt(_data.attribute("nickname").index());
	    _data.deleteAttributeAt(_data.attribute("nbTickets").index());
	    _data.deleteAttributeAt(_data.attribute("noble").index());
	    _data.deleteAttributeAt(_data.attribute("soldier").index());
	    _data.deleteAttributeAt(_data.attribute("religious").index());
	    
		/* Détermination de l'attribut de classe (survived). ---------------- */
		int classIndex = _data.attribute("survived").index();
		_data.setClassIndex(classIndex);
	    
		/* DEBUG: Affichage des attributs. ---------------------------------- */
//		System.out.println("# Attributs :");
		@SuppressWarnings("unchecked")
		Enumeration<Attribute> att = _data.enumerateAttributes();
		_atts = new ArrayList<Attribute>();
		while (att.hasMoreElements()) {
			Attribute a = att.nextElement();
//			System.out.println(a);
			_atts.add(a);
		}

		/* DEBUG: Affichage de la classe. ----------------------------------- */
//		System.out.println("\n# Classes :");
		_classes = _data.classAttribute();
//		System.out.println(_classes);

		/* DEBUG: Affichage des instances. ---------------------------------- */
//		 System.out.println("\n# Instances :");
//		 @SuppressWarnings("unchecked")
//		 Enumeration<Instance> insts = _data.enumerateInstances();
//		 while(insts.hasMoreElements()) {
//			 Instance inst = insts.nextElement();
//			 System.out.println(inst);
//		 }
//		 System.out.println(_doubleTickets.size());
	}

	/**
	 * Constructeur par copie
	 * @param o Objet à copier
	 */
	@SuppressWarnings("unused")
	private Titanic(final Titanic o) {
		_data = new Instances(o._data);
		_atts = new ArrayList<Attribute>(o._atts);
		_classes = (Attribute) o._classes.copy();
	}
	
	/**
	 * Accesseur
	 * @return Données
	 */
	public Instances getData() {
		return _data;
	}

	/**
	 * Modificateur
	 * @param data Données
	 */
	public void setData(Instances data) {
		_data = data;

		@SuppressWarnings("unchecked")
		Enumeration<Attribute> att = _data.enumerateAttributes();
		_atts = new ArrayList<Attribute>();
		while (att.hasMoreElements()) {
			Attribute a = att.nextElement();
			_atts.add(a);
		}
	}

	/**
	 * Accesseurs
	 * @return Classes
	 */
	public Attribute getClasses() {
		return _classes;
	}

	/**
	 * Accesseur
	 * @return Attributs 
	 */
	public List<Attribute> getAttributes() {
		return _atts;
	}
	
	/**
	 * Supprime un attribut des données
	 * @param i Indice de l'attribut dans le jeu d'essai.
	 */
	public void deleteAttributeAt(int i) {
		_data.deleteAttributeAt(i);
		
		@SuppressWarnings("unchecked")
		Enumeration<Attribute> att = _data.enumerateAttributes();
		_atts = new ArrayList<Attribute>();
		while (att.hasMoreElements()) {
			Attribute a = att.nextElement();
			_atts.add(a);
		}
	}

	
	/**
	 * Exécution de l'algorithme Apriori
	 * @param seuilSupport Seuil de support
	 * @param seuilConfiance Seuil de confiance
	 * @param numRules Nombre de règles à générer
	 * @return Liste des règles générées comprenant partie gauche, partie droite 
	 * (classe) et confiance.
	 * 
	 * @throws Exception
	 */
	public List<List<Object>> apriori(
			double seuilSupport,
			double seuilConfiance, 
			int numRules
		) throws Exception {
		ArrayList<List<Object>> ret = new ArrayList<List<Object>>();

		// Recherche des règles d'association en attributs
		System.out.println("\n# Règles d'association :");
		Apriori ap = new Apriori();
		ap.setLowerBoundMinSupport(seuilSupport);
		ap.setMinMetric(seuilConfiance);
		//ap.setCar(true);
		ap.setNumRules(numRules);

		long start = System.currentTimeMillis();
		ap.buildAssociations(_data);
		long end = System.currentTimeMillis();

		// Récupération des règles générées
		FastVector[] rules = ap.getAllTheRules();
//		FastVector[] rules = ap.mineCARs(_data);

		// Opérandes de gauche pour chaque rôle
		FastVector lefts = rules[0];
		// Opérandes de droite pour chaque rôle
		FastVector rights = rules[1];
		// Confiance pour chaque règle
		FastVector confs = rules[2];

		int nbRules = lefts.size();

		// System.out.println(ap);

		for (int i = 0; i < nbRules; i++) {
			ItemSet left = (ItemSet) lefts.elementAt(i);
			ItemSet right = (ItemSet) rights.elementAt(i);
			double conf = (double) confs.elementAt(i);
			List<Object> retRule = new ArrayList<Object>();
			retRule.add(0, left);
			retRule.add(1, right);
			retRule.add(2, conf);
			ret.add(retRule);
			System.out.print(i + 1 + ". ");
			for (int j = 0; j < _data.numAttributes() - 1; j++) {
				int value = left.itemAt(j);
				if (value != -1) {
					System.out.print(
						_atts.get(j).name() + "=" + 
						_atts.get(j).value(value) + " "
					);
				}
			}
			System.out.println(" => " + _classes.value(right.itemAt(0))
					+ " conf(" + conf + ")");
		}

		System.out.println("Temps exécution Apriori : " + (end - start) + "ms");

		return ret;
	}

	/**
	 * Vérifie qu'une règle est semblable avec une nouvelle liste de règles.
	 * 
	 * @param rule Règle à vérifier
	 * @param newRuleList Liste des règles avec laquelle nous allons comparer
	 * @param position La position de l'élément que l'on vient d'enlever
	 * @return Vrai si dissemblable, faux sinon.
	 */
	public static boolean compare(List<Object> rule, List<List<Object>> newRuleList,
			int position) {
		ItemSet left = (ItemSet) rule.get(0);
		int lefts[] = left.items();
		ItemSet right = (ItemSet) rule.get(1);
		double conf = (double) rule.get(2);
		for (int i = 0; i < newRuleList.size(); i++) {
			List<Object> currentRule = newRuleList.get(i);
			ItemSet currentLeft = (ItemSet) currentRule.get(0);
			ItemSet currentRight = (ItemSet) currentRule.get(1);
			double currentConf = (double) currentRule.get(2);
			int clefts[] = currentLeft.items();
			boolean good = true;// on considere que c'est la regle qu'on cherche
			for (int j = 0; j < lefts.length; j++) {
				boolean found = false;// on considere que l'element qu'on
										// regarde n'existe pas
				for (int k = 0; k < clefts.length; k++) {
					// si l'element existe dans les deux regles ou c'est
					// l'element dont on vient d'enlever on met found a true
					if ((lefts[j] == clefts[k]) && (clefts[k] == position))
					{
						found = true;
					}
				}
				if (!found) { // s'il existe un element different c'est pas la
								// bonne regle
					good = false;
				}
			}
			if (good)// si nous avons trouve la regle nous verifions pour voir
						// si l'element est discriminant (cf tableau rapport)
			{
				if (currentRight.itemAt(0) == right.itemAt(0)) {
					if (Math.abs(currentConf - conf) > 0.4)
						return true;
					else
						return false;
				} else {
					if (Math.abs(currentConf - conf) > 0.4)
						return false;
					else
						return true;
				}
			}
		}
		return true;// si nous trouvons aucune regle qui est genere a la fin de
					// la comparaison
		// l'element qu'on vient d'enlever est discriminant
	}

	Set<String> getValues(int numAtt) {
		Set<String> attValues = new HashSet<String>();
		@SuppressWarnings("unchecked")
		Enumeration<Instance> insts = _data.enumerateInstances();
		
//		System.out.println(_atts.get(numAtt).name() + ":");
		
		while(insts.hasMoreElements()) {
			Instance inst = insts.nextElement();
			 
			Attribute att = inst.attribute(numAtt + 1);
			String value = inst.toString(att);
			if(!attValues.contains(value)) {
//				if(value.equals("?"))
//					System.out.println("UNKNOWN FOUND !");
				attValues.add(value);
			}
		 }
		
		return attValues;
	}
	
	/**
	 * Renseigne l'attribut nbTickets du passger
	 */
	public void setNbTickets (Map<String, Integer> hm)
	{
		 @SuppressWarnings("unchecked")
		 Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();
			 
			 String ticket = inst.toString(_data.attribute("ticket"));
			 
			 inst.setValue(_data.attribute("nbTickets").index(), hm.get(ticket));
	 }
	}
	
	/**
	 * Renseigne l'attribut Title du titre du passager.
	 * 
	 * @TODO: grouper avec autres traitements
	 */
	public void setSplittedNameValuesInInstances() {
		@SuppressWarnings("unchecked")
		Enumeration<Instance> insts = _data.enumerateInstances();
		Set<String> nobleTitles = new HashSet<String>();
		nobleTitles.add("Don.");
		nobleTitles.add("Lady.");
		nobleTitles.add("Sir.");
		nobleTitles.add("the Countess.");
		nobleTitles.add("Jonkheer.");
		nobleTitles.add("Master.");
		nobleTitles.add("Dr.");
		Set<String> soldierTitles = new HashSet<String>();
		soldierTitles.add("Major.");
		soldierTitles.add("Col.");
		soldierTitles.add("Capt.");
		Set<String> religiousTitles = new HashSet<String>();
		religiousTitles.add("Rev.");

		while (insts.hasMoreElements()) {
			Instance inst = insts.nextElement();

			String name = inst.toString(_data.attribute("name"));

			Pattern p = Pattern
					.compile("\\'([A-Za-z \\'\\-]+), ([A-Za-z ]+\\.) ([A-Za-z ]+).*\\'");
			Matcher m = p.matcher(name);
			String surname = "";
			String title = "";
			String firstname = "";
			String maidenName = "";
			String nickname = "";

			if (m.matches()) {
				surname = m.group(1);
				title = m.group(2);
				firstname = m.group(3);
			} else {
				p = Pattern.compile("\\'([A-Za-z \\'\\-]+), ([A-Za-z ]+\\.).*\\'");
				m = p.matcher(name);
				if (m.matches()) {
					surname = m.group(1);
					title = m.group(2);
				}
			}

			p = Pattern.compile("\\'.*\\(([A-Za-z ]+)\\).*\\'");
			m = p.matcher(name);
			if (m.matches()) {
				maidenName = m.group(1);
			}

			p = Pattern.compile(".*\\\\'([A-Za-z ]+)\\\\'.*");
			m = p.matcher(name);
			if (m.matches()) {
				nickname = m.group(1);
			}

//			System.out.println("#'"+name+"'");
//			System.out.println("'"+surname+"'");
//			System.out.println("'"+title+"'");
//			System.out.println("'"+firstname+"'");
//			System.out.println("'"+maidenName+"'");
//			System.out.println("'"+nickname+"'");
			if(!surname.equals(""))
				inst.setValue(_data.attribute("surname").index(), surname);
			if(!title.equals("")) {
				inst.setValue(_data.attribute("title").index(), title);
				inst.setValue(_data.attribute("noble").index(), nobleTitles.contains(title)?"1":"0");
				inst.setValue(_data.attribute("soldier").index(), soldierTitles.contains(title)?"1":"0");
				inst.setValue(_data.attribute("religious").index(), religiousTitles.contains(title)?"1":"0");
			}
			if(!firstname.equals(""))
				inst.setValue(_data.attribute("firstname").index(), firstname);
			if(!maidenName.equals(""))
				inst.setValue(_data.attribute("maidenName").index(), maidenName);
			if(!nickname.equals(""))
				inst.setValue(_data.attribute("nickname").index(), nickname);
		}
	}
	

	/**
	 * Remplace les données inconnus par "UNKNOWN".
	 */
	public void setUnknown() {
		 @SuppressWarnings("unchecked")
		 Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();

			 String age = inst.toString(_data.attribute("age"));
			 String cabin = inst.toString(_data.attribute("cabin"));
			 String embarked = inst.toString(_data.attribute("embarked"));
			 
			 if("?".equals(age)) {
				 inst.setValue(_data.attribute("age").index(), "UNKNOWN");
			 }
			 if("?".equals(cabin)) {
				 inst.setValue(_data.attribute("cabin").index(), "UNKNOWN");
			 }
			 if("?".equals(embarked)) {
				 inst.setValue(_data.attribute("embarked").index(), "UNKNOWN");
			 }
		 }
	}

	/**
	 * Découpe l'ensemble des noms apparaissant dans le jeu de données.
	 * @return Ensemble des Noms, Prénoms, Titres, Noms de jeune fille et surnom.
	 */
	public Map<String, FastVector> splitNameValues() {
		Map<String, FastVector> result = new HashMap<String, FastVector>();
		FastVector surnameVals = new FastVector();
		FastVector titleVals = new FastVector();
		FastVector firstnameVals = new FastVector();
		FastVector maidenNameVals = new FastVector();
		FastVector nicknameVals = new FastVector();
		
		Attribute nameAtt = _data.attribute("name");
		int nbNames = nameAtt.numValues();
		for(int i = 0; i < nbNames; i++) {
			String name = nameAtt.value(i);
			 
			Pattern p = Pattern.compile("([A-Za-z \\'\\-]+), ([A-Za-z ]+\\.) ([A-Za-z ]+).*");
			Matcher m = p.matcher(name);
			String surname = "";
			String title = "";
			String firstname = "";
			String maidenName = "";
			String nickname = "";
			
			if(m.matches()) {
				surname = m.group(1);
				title = m.group(2);
				firstname = m.group(3);
			} else {
				p = Pattern.compile("([A-Za-z \\'\\-]+), ([A-Za-z ]+\\.).*");
				m = p.matcher(name);
				if(m.matches()) {
					surname = m.group(1);
					title = m.group(2);
				}
			}
			
			p = Pattern.compile(".*\\(([A-Za-z ]+)\\).*");
			m = p.matcher(name);
			if(m.matches()) {
				maidenName = m.group(1);
			}

			p = Pattern.compile(".*\\'([A-Za-z ]+)\\'.*");
			m = p.matcher(name);
			if(m.matches()) {
				nickname = m.group(1);
			}

			if(!surname.equals("")&&!surnameVals.contains(surname)) {
				surnameVals.addElement(surname);
			}
			if(!title.equals("")&&!titleVals.contains(title)) {
				titleVals.addElement(title);
			}
			if(!firstname.equals("")&&!firstnameVals.contains(firstname)) {
				firstnameVals.addElement(firstname);
			}
			if(!maidenName.equals("")&&!maidenNameVals.contains(maidenName)) {
				maidenNameVals.addElement(maidenName);
			}
			if(!nickname.equals("")&&!nicknameVals.contains(nickname)) {
				nicknameVals.addElement(nickname);
			}
		}

		result.put("surname", surnameVals);
		result.put("title", titleVals);
		result.put("firstname", firstnameVals);
		result.put("maidenName", maidenNameVals);
		result.put("nickname", nicknameVals);
		
		return result;
	}
	
	/**
	 * Renseigne l'attribut alone qui informe si le passager voyage seul ou pas
	 */
	public void setTravelsAlone(){
		@SuppressWarnings("unchecked")
		Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();
			 
			 int sibsp = Integer.parseInt(inst.toString(_data.attribute("sibsp")));
			 int parch = Integer.parseInt(inst.toString(_data.attribute("parch")));
			 
			 if(sibsp==0 && parch==0)
			 {
				 inst.setValue(_data.attribute("alone").index(), "1");
			 }
			 else
			 {
				 inst.setValue(_data.attribute("alone").index(), "0");
			 }
		 }
	}
	
	/**
	 * Renseigne l'attribut etage qui informe la distance de la cabine au pont
	 */
	public void setEtageValues()
	{
		@SuppressWarnings("unchecked")
		Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();
			 
			 String etage = inst.toString(_data.attribute("cabin"));
			 
			 if(etage.contains("G"))
				 inst.setValue(_data.attribute("etage").index(), "7");
			 else if (etage.contains("F"))
				 inst.setValue(_data.attribute("etage").index(), "6");
			 else if (etage.contains("E"))
				 inst.setValue(_data.attribute("etage").index(), "5");
			 else if (etage.contains("D"))
				 inst.setValue(_data.attribute("etage").index(), "4");
			 else if (etage.contains("C"))
				 inst.setValue(_data.attribute("etage").index(), "3");
			 else if (etage.contains("B"))
				 inst.setValue(_data.attribute("etage").index(), "2");
			 else if (etage.contains("A"))
				 inst.setValue(_data.attribute("etage").index(), "1");
			 else
				 inst.setValue(_data.attribute("etage").index(), "-1");
		 }
	}
	
	protected void save(String filename)
			throws Exception {
		BufferedWriter writer;

		writer = new BufferedWriter(new FileWriter(filename));
		writer.write(_data.toString());
		writer.newLine();
		writer.flush();
		writer.close();
	}
	
	/**
	 * Renseigne l'attribut Title du titre du passager.
	 * @TODO: grouper avec autres traitements
	 */
	public void setTicketValuesInInstances() {
		 @SuppressWarnings("unchecked")
		 Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();
			 
			 String ticket = inst.toString(_data.attribute("ticket"));
			 
			 System.out.println("#" + ticket);
						 
			 inst.setValue(inst.numAttributes() - 1, ticket);
		 }
	}
	
	/**
	 * Récupere l'ensemble des tickets apparaissant dans le jeu de données.
	 * @return Ensemble des titres
	 * @TODO: Extraire aussi Nom et PrÈnom
	 */
	public Map<String, Integer> getTicketValues() {
		
		FastVector ticketVals = new FastVector();
		FastVector allTicketVals = new FastVector();
		Map<String, Integer> hm = new HashMap<String, Integer>();
		int i = 0;
		 @SuppressWarnings("unchecked")
		 Enumeration<Instance> insts = _data.enumerateInstances();
		 
		 while(insts.hasMoreElements()) {
			 Instance inst = insts.nextElement();
			 
			 String ticket = inst.toString(_data.attribute("ticket"));
			 
			 if (hm.get(ticket)!=null)
			 {
				 int nr = (int) hm.get(ticket);
				 nr++;
				 hm.put(ticket, nr);
			 }
			 else
			 {
				 hm.put(ticket, 1);
			 }
			 //System.out.println("#" + ticket);
		 }
		 
//		 System.out.println("nr doublon " + i);	 
		return hm;
	}
	
	/**
	 * Méthode de prédiction de la survie à partir d'un jeu d'entrainement.
	 * @param train Jeu d'entrainement.
	 * @throws Exception 
	 */
	public void predict(Titanic train) throws Exception {
		 /* Classifieur J48. */
		 J48 j48 = new J48();
		 j48.setUnpruned(true);
		 
		 /* Meta-classifieur */
		 FilteredClassifier fc = new FilteredClassifier();
		 fc.setClassifier(j48);
		 
		 /* Entrainement et réalisation des prédictions. */
		 fc.buildClassifier(train.getData());
		 for (int i = 0; i < _data.numInstances(); i++) {
			 int pred = (int) fc.classifyInstance(_data.instance(i));
			 Instance inst = _data.instance(i);

			 /* 
			  * Quelques suppositions à partir de règles APriori, sinon on s'en
			  * remet à J48
			  */
			 if(
				 inst.toString(_data.attribute("sex")).equals("female") &&
				 !inst.toString(_data.attribute("pclass")).equals("3")
			 ) {
				 inst.setValue(_data.classIndex(), "1");
			 } else if(
				 inst.toString(_data.attribute("sex")).equals("male") &&
				 inst.toString(_data.attribute("pclass")).equals("2")
			 ) {
				 inst.setValue(_data.classIndex(), "0");
			 } else {
				 inst.setValue(_data.classIndex(), Integer.toString(pred));
			 }
		 }
	}
}
